package cn.flkj.luciagm1v4v1.intermediate;

import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.hyperledger.fabric.sdk.*;
import org.hyperledger.fabric.sdk.exception.InvalidArgumentException;
import org.hyperledger.fabric.sdk.exception.ProposalException;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

import static java.nio.charset.StandardCharsets.UTF_8;

/**
 * 中继合约对象
 * 
 * @author : 州长在手 2021/1/14 上午10:58
 */
@Slf4j
public class IntermediateChaincodeID {

    /**
     * 单个提案请求的超时时间以毫秒为单位
     */
    private static final int proposalWaitTime = 20000;


//
//    public void setChaincodeID() {
//
//        if (chaincodeName.length() > 0 && chaincodePath.length() > 0 && chaincodeVersion.length() > 0) {
//            chaincodeID = ChaincodeID.newBuilder().setName(chaincodeName).setVersion(chaincodeVersion).setPath(chaincodePath).build();
//        } else if (chaincodeName.length() > 0) {
//            chaincodeID = ChaincodeID.newBuilder().setName(chaincodeName).build();
//        }
//    }

    /**
     * 获取带有版本号的chaincodeID
     * @param chaincodeName
     * @param chaincodePath 相对于GOPATH的路径
     * @param chaincodeVersion
     * @return
     */
    public static  ChaincodeID chaincodeID(String chaincodeName ,String chaincodePath ,String chaincodeVersion ){
        return ChaincodeID.newBuilder().setName(chaincodeName).setVersion(chaincodeVersion).setPath(chaincodePath).build();
    }

    /**
     * 默认chaincodeID
     * @param chaincodeName
     * @return
     */
    public static  ChaincodeID chaincodeID(String chaincodeName ){
        return ChaincodeID.newBuilder().setName(chaincodeName).build();
    }

    /**
     * 安装智能合约
     *
     */
    public static JSONObject installGolang(HFClient client, Channel channel , String chaincodeName,String chaincodeVersion , String chaincodePath,InputStream chaincodeTarGzInputStream)  {
        /// Send transaction proposal to all peers
        try {
            InstallProposalRequest installProposalRequest =client.newInstallProposalRequest();
            //installProposalRequest.setChaincodeName(chaincodeName);
           // installProposalRequest.setChaincodeVersion(chaincodeVersion);
           // installProposalRequest.setChaincodePath(chaincodePath);
            installProposalRequest.setChaincodeID(ChaincodeID.newBuilder().setName(chaincodeName).setVersion(chaincodeVersion).setPath(chaincodePath).build());
            installProposalRequest.setChaincodeLanguage(TransactionRequest.Type.GO_LANG);
            installProposalRequest.setProposalWaitTime(proposalWaitTime);
//            installProposalRequest.setChaincodeSourceLocation(new File(System.getenv("GOPATH")));
//            installProposalRequest.setChaincodeMetaInfLocation(new File("src/test/fixture/meta-infs/end2endit"));
//           if (   installProposalRequest.getChaincodeSourceLocation() != null ){
//           }
            installProposalRequest.setChaincodeInputStream(chaincodeTarGzInputStream);
            Collection<ProposalResponse> installProposalResponses = client.sendInstallProposal(installProposalRequest, channel.getPeers());
            return toPeerResponse(installProposalResponses, false);
        }catch (InvalidArgumentException e){
            log.error("GOPATH 是否设置 \n "+e);
            return InUtils.RespError(e.getMessage(), ":)");
        }catch (ProposalException e){
            log.error("发送到peer错误，检测参数和peer状态，确保链接到peer \n "+e);
            return InUtils.RespError(e.getMessage(), ":)");
        }

    }


    /**
     * 实例化智能合约
     *
     * @param client
     * @param channel
     * @param endorsementPolicyInputStream 就因该用数据流
     * @param chaincodeId 使用本类的函数创建
     * @param args
     * @return
     */
    public static JSONObject instantiate(HFClient client, Channel channel , InputStream endorsementPolicyInputStream, ChaincodeID chaincodeId, String ... args)   {
        try {
            /// Send transaction proposal to all peers
            InstantiateProposalRequest instantiateProposalRequest = client.newInstantiationProposalRequest();
            instantiateProposalRequest.setChaincodeID(chaincodeId);
            instantiateProposalRequest.setProposalWaitTime(proposalWaitTime);
            instantiateProposalRequest.setArgs(args);

            ChaincodeEndorsementPolicy chaincodeEndorsementPolicy = new ChaincodeEndorsementPolicy();
            // 这选择了数据流的API
            chaincodeEndorsementPolicy.fromStream(endorsementPolicyInputStream);

            instantiateProposalRequest.setChaincodeEndorsementPolicy(chaincodeEndorsementPolicy);

            Map<String, byte[]> tm2 = new HashMap<>();
            tm2.put("HyperLedgerFabric", "InstantiateProposalRequest:JavaSDK".getBytes(UTF_8));
            tm2.put("method", "InstantiateProposalRequest".getBytes(UTF_8));
            tm2.put("result", ":)".getBytes(UTF_8));
            instantiateProposalRequest.setTransientMap(tm2);
            Collection<ProposalResponse> instantiateProposalResponses = channel.sendInstantiationProposal(instantiateProposalRequest, channel.getPeers());
            return toOrdererResponse(instantiateProposalResponses, channel);
        }catch (IOException e ){
            log.error("策略文件错误，InputStream 正确， "+e);
            return InUtils.RespError(e.getMessage(), ":)");
        }catch (InvalidArgumentException e){
            log.error("设置了无效参数， "+e);
            return InUtils.RespError(e.getMessage(), ":)");
        }catch (ProposalException e){
            log.error("发送数据出错， "+e);
            return InUtils.RespError(e.getMessage(), ":)");
        }

    }

    /**
     * 升级智能合约,其实就是先安装在实例化的过程，只不过可以用同一个文件名
     *
     */
    public static JSONObject upgrade(HFClient client, Channel channel , InputStream endorsementPolicyInputStream, String  chaincodeName ,String version, String chaincodePath, InputStream chaincodeTarGzInputStream, String ... args){
        try {
            InstallProposalRequest installProposalRequest =client.newInstallProposalRequest();
            //installProposalRequest.setChaincodeName(chaincodeName);
            // installProposalRequest.setChaincodeVersion(chaincodeVersion);
            // installProposalRequest.setChaincodePath(chaincodePath);
            installProposalRequest.setChaincodeID(ChaincodeID.newBuilder().setName(chaincodeName).setVersion(version).setPath(chaincodePath).build());
            installProposalRequest.setChaincodeLanguage(TransactionRequest.Type.GO_LANG);
            installProposalRequest.setProposalWaitTime(proposalWaitTime);
//            installProposalRequest.setChaincodeSourceLocation(new File(System.getenv("GOPATH")));
//            installProposalRequest.setChaincodeMetaInfLocation(new File("src/test/fixture/meta-infs/end2endit"));
//           if (   installProposalRequest.getChaincodeSourceLocation() != null ){
//           }
            installProposalRequest.setChaincodeInputStream(chaincodeTarGzInputStream);
         //   Collection<ProposalResponse> installProposalResponses =
            client.sendInstallProposal(installProposalRequest, channel.getPeers());
            //return toPeerResponse(installProposalResponses, false);

            /// Send transaction proposal to all peers

            UpgradeProposalRequest upgradeProposalRequest = client.newUpgradeProposalRequest();

            upgradeProposalRequest.setChaincodeID(ChaincodeID.newBuilder().setName(chaincodeName).setVersion(version).build());

            upgradeProposalRequest.setProposalWaitTime(proposalWaitTime);
            upgradeProposalRequest.setArgs(args);
            ChaincodeEndorsementPolicy chaincodeEndorsementPolicy = new ChaincodeEndorsementPolicy();
            chaincodeEndorsementPolicy.fromStream(endorsementPolicyInputStream);
            upgradeProposalRequest.setChaincodeEndorsementPolicy(chaincodeEndorsementPolicy);
            Map<String, byte[]> tm2 = new HashMap<>();
            tm2.put("HyperLedgerFabric", "UpgradeProposalRequest:JavaSDK".getBytes(UTF_8));
            tm2.put("method", "UpgradeProposalRequest".getBytes(UTF_8));
            tm2.put("result", ":)".getBytes(UTF_8));
            upgradeProposalRequest.setTransientMap(tm2);
            Collection<ProposalResponse> upgradeProposalResponses = channel.sendUpgradeProposal(upgradeProposalRequest,channel.getPeers());
            return toOrdererResponse(upgradeProposalResponses, channel);

        }catch (InvalidArgumentException | IOException e){
        log.error("检测路径 \n "+e);
        return InUtils.RespError(e.getMessage(), ":)");
        }catch (ProposalException e){
        log.error("发送到peer错误，检测参数和peer状态，确保链接到peer \n "+e);
        return InUtils.RespError(e.getMessage(), ":)");
    }

    }
//    JSONObject upgrade(HFClient client, Channel channel, InputStream endorsementPolicyInputStream,  ChaincodeID chaincodeID ,String...args)  {
//
//        try{
//        /// Send transaction proposal to all peers
//        UpgradeProposalRequest upgradeProposalRequest = client.newUpgradeProposalRequest();
//        upgradeProposalRequest.setChaincodeID(chaincodeID);
//        upgradeProposalRequest.setProposalWaitTime(proposalWaitTime);
//        upgradeProposalRequest.setArgs(args);
//
//        ChaincodeEndorsementPolicy chaincodeEndorsementPolicy = new ChaincodeEndorsementPolicy();
//        chaincodeEndorsementPolicy.fromStream(endorsementPolicyInputStream);
//        upgradeProposalRequest.setChaincodeEndorsementPolicy(chaincodeEndorsementPolicy);
//
//        Map<String, byte[]> tm2 = new HashMap<>();
//        tm2.put("HyperLedgerFabric", "UpgradeProposalRequest:JavaSDK".getBytes(UTF_8));
//        tm2.put("method", "UpgradeProposalRequest".getBytes(UTF_8));
//        tm2.put("result", ":)".getBytes(UTF_8));
//        upgradeProposalRequest.setTransientMap(tm2);
//        Collection<ProposalResponse> upgradeProposalResponses = channel.sendUpgradeProposal(upgradeProposalRequest,channel.getPeers());
//        return toOrdererResponse(upgradeProposalResponses, channel);
//
//        }catch (IOException e ){
//            log.error("策略文件错误，InputStream 正确， "+e);
//            return InUtils.RespError(e.getMessage(), ":)");
//        }catch (InvalidArgumentException e){
//            log.error("设置了无效参数， "+e);
//            return InUtils.RespError(e.getMessage(), ":)");
//        }catch (ProposalException e){
//            log.error("发送数据出错， "+e);
//            return InUtils.RespError(e.getMessage(), ":)");
//        }
//    }



    /**
     *  执行智能合约本来就应该是静态函数
     * @param client
     * @param channel
     * @param userContext
     * @param chaincodeID
     * @param fcn
     * @param args
     * @return
     * @throws InvalidArgumentException
     * @throws ProposalException
     * @throws UnsupportedEncodingException
     */
    public static JSONObject invoke(HFClient client, Channel channel, User userContext, ChaincodeID chaincodeID, String fcn, String ...args) throws InvalidArgumentException, ProposalException, UnsupportedEncodingException {
        TransactionProposalRequest transactionProposalRequest = client.newTransactionProposalRequest();
        transactionProposalRequest.setChaincodeID(chaincodeID);
        transactionProposalRequest.setChaincodeLanguage(TransactionRequest.Type.GO_LANG);
        transactionProposalRequest.setUserContext(userContext);
        transactionProposalRequest.setFcn(fcn);
        transactionProposalRequest.setProposalWaitTime(proposalWaitTime);
        transactionProposalRequest.setArgs(args);
        Collection<ProposalResponse> transactionPropResp = channel.sendTransactionProposal(transactionProposalRequest, channel.getPeers());
        return toOrdererResponse(transactionPropResp, channel);
    }

    /**
     * 查询智能合约
     */
    public static JSONObject query(HFClient client, Channel channel, String chaincodeName, String fcn, String[] args) throws InvalidArgumentException, ProposalException{

        QueryByChaincodeRequest queryByChaincodeRequest = client.newQueryProposalRequest();
        queryByChaincodeRequest.setArgs(args);
        queryByChaincodeRequest.setFcn(fcn);
        queryByChaincodeRequest.setChaincodeID(ChaincodeID.newBuilder().setName(chaincodeName).build());
        queryByChaincodeRequest.setChaincodeName(chaincodeName);
        Collection<ProposalResponse> queryProposals = channel.queryByChaincode(queryByChaincodeRequest, channel.getPeers());
        return toPeerResponse(queryProposals, true);

    }


    /**
     * 获取实例化合约、升级合约以及invoke合约的返回结果集合
     *
     * @param proposalResponses 请求返回集合
     */
    private static JSONObject toOrdererResponse(Collection<ProposalResponse> proposalResponses, Channel channel) throws InvalidArgumentException, UnsupportedEncodingException {
        JSONObject jsonObject = new JSONObject();
        Collection<ProposalResponse> successful = new LinkedList<>();
        Collection<ProposalResponse> failed = new LinkedList<>();
        for (ProposalResponse response : proposalResponses) {
            if (response.getStatus() == ProposalResponse.Status.SUCCESS) {
                successful.add(response);
            } else {
                failed.add(response);
            }
        }
        if (failed.size() > 0) {
            ProposalResponse firstTransactionProposalResponse = failed.iterator().next();
            jsonObject.put("code", InUtils.ERROR);
            jsonObject.put("error", firstTransactionProposalResponse.getMessage());
            return jsonObject;
        } else {
            ProposalResponse resp = proposalResponses.iterator().next();
            byte[] x = resp.getChaincodeActionResponsePayload();
            String resultAsString = null;
            if (x != null) {
                resultAsString = new String(x, "UTF-8");
            }
            channel.sendTransaction(successful);
            jsonObject = parseResult(resultAsString);
            jsonObject.put("code", InUtils.SUCCESS);
            jsonObject.put("txid", resp.getTransactionID());
            return jsonObject;
        }
    }

    /**
     * 获取安装合约以及query合约的返回结果集合
     *
     * @param proposalResponses 请求返回集合
     * @param checkVerified     是否验证提案
     */
    private static JSONObject toPeerResponse(Collection<ProposalResponse> proposalResponses, boolean checkVerified) {
        JSONObject jsonObject = new JSONObject();
        for (ProposalResponse proposalResponse : proposalResponses) {
            if ((checkVerified && !proposalResponse.isVerified()) || proposalResponse.getStatus() != ProposalResponse.Status.SUCCESS) {
                String data = String.format("Failed install/query proposal from peer %s status: %s. Messages: %s. Was verified : %s",
                        proposalResponse.getPeer().getName(), proposalResponse.getStatus(), proposalResponse.getMessage(), proposalResponse.isVerified());
                jsonObject.put("code", InUtils.ERROR);
                jsonObject.put("error", data);
            } else {
                String payload = proposalResponse.getProposalResponse().getResponse().getPayload().toStringUtf8();
                jsonObject = parseResult(payload);
                jsonObject.put("code", InUtils.SUCCESS);
                jsonObject.put("txid", proposalResponse.getTransactionID());
            }
        }
        return jsonObject;
    }

//    /**
//     * 设置单个提案请求的超时时间以毫秒为单位
//     *
//     * @param proposalWaitTime 超时时间以毫秒为单位
//     */
//    void setProposalWaitTime(int proposalWaitTime) {
//        this.proposalWaitTime = proposalWaitTime;
//    }

    private static JSONObject parseResult(String result) {
        JSONObject jsonObject = new JSONObject();
        int jsonVerify = isJSONValid(result);
        switch (jsonVerify) {
            case 0:
                jsonObject.put("data", result);
                break;
            case 1:
                jsonObject.put("data", JSONObject.parseObject(result));
                break;
            case 2:
                jsonObject.put("data", JSONObject.parseArray(result));
                break;
        }
        return jsonObject;
    }

    /**
     * 判断字符串类型
     *
     * @param str 字符串
     * @return 0-string；1-JsonObject；2、JsonArray
     */
    private static int isJSONValid(String str) {
        try {
            JSONObject.parseObject(str);
            return 1;
        } catch (JSONException ex) {
            try {
                JSONObject.parseArray(str);
                return 2;
            } catch (JSONException ex1) {
                return 0;
            }
        }
    }



}
